/*
	Copyright (C) 2011 Simas Toleikis

	This file is part of Die2Nite Agent.

	Die2Nite Agent is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

"use strict";

// External map applications
var mapApplications = {

	atlas: {
		name: "Atlas",
		id: 12,
		successMatch: /^1$/,
		updateURL: "http://atlas.wonderfulfailure.com/scripts/updater.php",
		keySelector: 'form[action*="atlas.wonderfulfailure.com"] input[name=key]'
	},
	ovalOffice: {
		name: "Oval Office",
		id: 10,
		successMatch: /map has been updated/i,
		updateURL: "http://d2n.sindevel.com/oo/upd.php",
		keySelector: 'form[action*="d2n.sindevel.com/oo"] input[name=key]'
	}
};

// Initialize options
var defaultOptions = {
	compactMode: false,
	expertMode: false,
	waitTime: 3
};

for (var applicationID in mapApplications) {
	defaultOptions["update-" + applicationID] = true;
}

var options = new Store("settings", defaultOptions);

// Attempt to get secure key from disclaimer page
function getSecureKeyFromDisclaimerPage(applicationID) {

	var ajaxRequest = {};

	ajaxRequest.type = "GET";
	ajaxRequest.cache = false;
	ajaxRequest.data = { id: mapApplications[applicationID].id };
	ajaxRequest.url = "http://www.die2nite.com/disclaimer";

	ajaxRequest.complete = (function(appID) { return function(xhr, textStatus) {

		var key = $(xhr.responseText).find(mapApplications[appID].keySelector).val();

		if (key) {
			localStorage.setItem("key-" + appID, key);
		}

	}})(applicationID);

	$.ajax(ajaxRequest);
}

for (var applicationID in mapApplications) {

	if (!localStorage.getItem("key-" + applicationID) && options.get("update-" + applicationID)) {
		getSecureKeyFromDisclaimerPage(applicationID);
	}
}

// Update map status icon and text
function updateMapStatus(port) {

	var message = '<div id="d2na-map-status-text">';
	message += "Current zone is synchronized with external map applications:<br />";
	message += "<ul>";

	var status = "updated";
	var isUpdating = false;
	var isFailed = false;

	for (var applicationID in mapApplications) {

		var application = mapApplications[applicationID];
		var applicationKey = localStorage.getItem("key-" + applicationID);

		if (applicationKey && application.status && options.get("update-" + applicationID)) {

			message += "<li" + (application.status == "failed" ? ' class="broken"' : "") + ">";
			message += '<img src="' + chrome.extension.getURL("img/icon-" + applicationID + ".png") + '" /> ';
			message += application.name + ": <strong>"

			switch (application.status) {

				case "updating": {
					message += "Updating...";
					isUpdating = true;
					break;
				}
				case "waiting": {
					message += "Waiting...";
					isUpdating = true;
					break;
				}
				case "updated": {
					message += "Updated!";
					break;
				}
				case "failed": {
					message += "Update failed!";
					isFailed = true;
					break;
				}
			}

			message += "</strong></li>";
		}
	}

	message += "</ul></div>";

	// Some applications are still updating
	if (isUpdating) {
		status = "updating";
	}
	// Application failed with update
	else if (isFailed) {
		status = "failed";
	}

	port.postMessage({type: "setMapStatus", status: status, message: message});
}

// Compare given zone state to previous one
function checkZoneState(newZoneState) {

	var zoneStateString = localStorage.getItem("zoneState");

	if (!zoneStateString) {
		return false;
	}

	var zoneState = JSON.parse(zoneStateString);
	if (!zoneState) {
		return false;
	}

	// Compare states
	var isStateEqual = true;
	for (var prop in newZoneState) {

		if (zoneState[prop] === undefined || zoneState[prop] !== newZoneState[prop]) {
			isStateEqual = false;
			break;
		}
	}

	return isStateEqual;
}

// Handle messages from content script (long-lived connections)
chrome.extension.onConnect.addListener(function(port) {

	port.onMessage.addListener(function(msg) {

		if (msg.type == "updateMap") {

			var keysAvailable = false;
			var updateEnabled = false;
			var isZoneChanged = !checkZoneState(msg.zoneState);

			for (var applicationID in mapApplications) {

				// Skip disabled applications
				if (!options.get("update-" + applicationID)) {
					mapApplications[applicationID].status = null;
					continue;
				}

				updateEnabled = true;
				var applicationKey = localStorage.getItem("key-" + applicationID);

				if (!applicationKey) {
					continue;
				}

				keysAvailable = true;

				if (!isZoneChanged && localStorage.getItem("lastUpdateFailed" + applicationID) == 0
						&& mapApplications[applicationID].status) {

					continue;
				}

				mapApplications[applicationID].status = "waiting";

				// AJAX request is scheduled (but not yet sent)
				if (mapApplications[applicationID].timeout) {
					clearTimeout(mapApplications[applicationID].timeout);
				}

				var ajaxRequest = {};

				ajaxRequest.type = "POST";
				ajaxRequest.cache = false;
				ajaxRequest.timeout = 30000;
				ajaxRequest.data = { key: applicationKey };
				ajaxRequest.url = mapApplications[applicationID].updateURL;

				ajaxRequest.complete = (function(appID) { return function(xhr, textStatus) {

					if (typeof xhr.responseText != "string" || !xhr.responseText.match(mapApplications[appID].successMatch)) {
						textStatus = "error";
					}

					if (textStatus == "success") {
						mapApplications[appID].status = "updated";
						localStorage.setItem("lastUpdateFailed" + appID, 0);
					}
					else {
						mapApplications[appID].status = "failed";
						localStorage.setItem("lastUpdateFailed" + appID, 1);
					}

					updateMapStatus(port);

				}})(applicationID);

				// Register timed future call
				mapApplications[applicationID].timeout = setTimeout((function(req, appID) { return function() {

					clearTimeout(mapApplications[appID].timeout);

					mapApplications[appID].timeout = null;
					mapApplications[appID].status = "updating";

					updateMapStatus(port);

					$.ajax(req);

				}})(ajaxRequest, applicationID), options.get("waitTime") * 1000);
			}

			// Update disabled: all external map applications are disabled in options
			if (!updateEnabled) {
				port.postMessage({type: "setMapStatus", status: "disabled"});
			}
			// No keys are available
			else if (!keysAvailable) {
				port.postMessage({type: "setMapStatus", status: "failed"});
			}
			else {
				localStorage.setItem("zoneState", JSON.stringify(msg.zoneState));
				updateMapStatus(port);
			}
		}
		// Check secure API keys
		else if (msg.type == "checkKeys") {

			for (var applicationID in mapApplications) {

				var key = localStorage.getItem("key-" + applicationID);

				// Report missing key if last update failed
				if (localStorage.getItem("lastUpdateFailed" + applicationID) == 1) {
					key = null;
				}

				port.postMessage({
					type: msg.type,
					key: key,
					application: applicationID,
					keySelector: mapApplications[applicationID].keySelector
				});
			}
		}
		// Store secure API key
		else if (msg.type == "setKey") {
			localStorage.setItem("key-" + msg.application, msg.key);
		}
	});
});

// Handle messages from content script (simple connections)
chrome.extension.onRequest.addListener(function(msg, sender, sendResponse) {

	// Check content mode
	if (msg.type == "getContentMode") {

		sendResponse({
			type: msg.type,
			compactMode: options.get("compactMode"),
			expertMode: options.get("expertMode")
		});
	}
	else {
		sendResponse({});
	}
});